﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

using agree.schema;
using agree.mrs;

using alib.BitArray;
using alib.Collections;
using alib.Collections.ReadOnly;
using alib.Combinatorics;
using alib.Debugging;
using alib.Enumerable;
using alib.Reflection;
using alib.String;

namespace agree.transfer
{
	using rel_base = Relation;
	using inp_rel = Relation;
	using rule_rel = Relation;
	using rule_tgt_rel = Relation;
	using rule_src_rel = Relation;
	using locus = Relation;

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// 
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public sealed class TransferQueue : ObservableQueue<Hypothesis>, IIdentity
	{
		public TransferQueue(TransferExecutor tex, Action<Hypothesis> submit)
		{
			this.tex = tex;
			this.submit = submit;

			base.CollectionChanged += (o, e) => ((TransferQueue)o).q_change(e.Action);
		}

		readonly TransferExecutor tex;
		readonly Action<Hypothesis> submit;

		void q_change(NotifyCollectionChangedAction action)
		{
			if (!tex.f_stop && action == NotifyCollectionChangedAction.Add)
				submit(Dequeue());
		}

		public IIdentity Trace { get { return tex; } }
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// 
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	[DebuggerDisplay("{ToString(),nq}")]
	public struct InpRulRelPair
	{
		public inp_rel inp;
		public rule_rel rul;
		public override String ToString()
		{
			return String.Format("{0}  -  {1}", inp, rul);
		}
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// 
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	[DebuggerDisplay("{ToString(),nq}")]
	public struct RuleApplication : IEquatable<RuleApplication>
	{
		public TransferRule rule;
		public int[] rg_inp_rel_ix;

		public bool Equals(RuleApplication other)
		{
			if (rule == null)
				return other.rule == null;
			int c;
			if (rule != other.rule || (c = rg_inp_rel_ix.Length) != other.rg_inp_rel_ix.Length)
				return false;
			for (int i = 0; i < c; i++)
				if (rg_inp_rel_ix[i] != other.rg_inp_rel_ix[i])
					return false;
			return true;
		}
		public override bool Equals(object obj)
		{
			return obj is RuleApplication && Equals((RuleApplication)obj);
		}
		public override int GetHashCode()
		{
			if (rule == null)
				return 0;
			int h = rule.GetHashCode();
			for (int i = 0; i < rg_inp_rel_ix.Length; i++)
			{
				int j = (rg_inp_rel_ix[i] + 1) << (i + 1);
				h ^= (h << j) ^ (h >> (32 - j));
			}
			return h;
		}
		public override String ToString()
		{
			if (rule == null)
				return "(null rule)";
			return String.Format("{0}[{1}]", rule.Name, rg_inp_rel_ix.StringJoin(","));
		}
	};


	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// 
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public static class xfer_ext
	{
		[DebuggerStepThrough]
		public static void dbg_write(String fmt, params Object[] args)
		{
			if (false)
				return;
			fmt = fmt.Replace(" { ", " {{ ");
			Debug.Write(String.Format(fmt, args));
		}
		[DebuggerStepThrough]
		public static void dbg_writeline(String fmt, params Object[] args)
		{
			dbg_write(fmt + Environment.NewLine, args);
		}
		[DebuggerStepThrough]
		public static void dbg_writeline()
		{
			dbg_write(Environment.NewLine);
		}


		public static void show_rels<T>(this bag<T> _this) where T : MrsElement
		{
			show_rels(_this.a);
		}

		public static void show_rels(this MrsElement[] _this)
		{
			foreach (var part in alib.Array.arr.Divide(_this, 7))
				_show_rels(part);
		}

		static void _show_rels(MrsElement[] _this)
		{
			int c_rels = _this.Length;
			int i, j, width, j_max = 0, i_jmax = -1;
			MrsElement rel;

			int[] rmw = new int[c_rels];
			for (i = 0; i < c_rels; i++)
			{
				rel = _this[i];

				if ((j = rel.Features._Count()) >= j_max)
				{
					j_max = j;
					i_jmax = i;
				}
				rmw[i] = j == 0 ? 1 : rel.Features.Max(y => y.Name.Length + y.Value.Name._Length()) + 1;
			}
			j_max++;

			//dbg_writeline(_this.Select((_rel, ix) => _rel.RelTypeName.TrimEndOrPadRight(rmw[ix] + 4)).StringJoin(" "));

#if true
			for (i = 0; i < c_rels; i++)
			{
				if (i > 0)
					dbg_write(" ");
				rel = _this[i];
				if (rel is arrow_rel)
					dbg_write(" ");
				else
					dbg_write("┌─" + new String(' ', rmw[i]) + "─┐");
			}
			dbg_writeline();
#else
			dbg_writeline(rmw.Select(w => "┌─" + new String(' ', w) + "─┐").StringJoin(" "));
#endif
			for (j = 0; j < j_max; j++)
			{
				for (i = 0; i < c_rels; i++)
				{
					rel = _this[i];
					width = rmw[i];
					var rc = rel.Features._Count();

					if (i > 0)
						dbg_write(" ");

					if (rel is arrow_rel)
					{
						if (j == (j_max / 2) - 1)
							dbg_write("→".PadCenter(width, ' '));
						else
							dbg_write(new String(' ', width));
					}
					else
					{
						if (j < rc)
							dbg_write("│ " + rel.Features.ElementAt(j).FormatLR(width) + " │");
						else if (j == rc)
							dbg_write("└─" + new String(' ', width) + "─┘");
						else if (i != i_jmax)
							dbg_write(new String(' ', width + 4));
						else
							goto done;
					}
				}
			done:
				dbg_writeline();
			}
		}

		public static bool Subsumes(this String super, String sub)
		{
			if (super == null || super == sub)
				return true;

			var glb = Unify(super, sub);
			return glb != null && glb == sub;
		}

		public static String Unify(this String a, String b)
		{
			if (a == b || a == null)
				return b;
			if (b == null)
				return a;

			String ta, tb;
			int q;
			if ((q = String.CompareOrdinal(ta = a, tb = b)) == 0)
				return a;
#if true
			return null;
#else
			if (a is Var || b is Var)
				return null;

			if (q < 0)
				return new type(ta + new String('=', Math.Max(0, tb.Length - ta.Length)));
			if (q > 0)
				return new type(tb + new String('=', Math.Max(0, ta.Length - tb.Length)));

			return a;
#endif
		}

		public static String FormatLR(this IProperty _this, int width)
		{
			String n, v;
			return (n = _this.Name) + new String(' ', width - n.Length - (v = _this.Value.Name)._Length()) + v;
			// (var is Variable ? ((Variable)var).Name : var.Type);
		}
	}


	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary> display helper </summary>
	[DebuggerDisplay("{ToString(),nq}")]
	public class _inp_refs : List<NodePtr>
	{
		public override String ToString()
		{
			return this[0].var.ToString() + ": { " + this.Select(np => np.RelFeatInf).StringJoin(";  ") + " }";
		}
	};

	public sealed class arrow_rel : MrsElement
	{
		static arrow_rel()
		{
			Instance = new arrow_rel();
		}
		public static MrsElement Instance;
		arrow_rel() : base(default(Mrs)) { }
		public override string Name { get { return ""; } }
		public override int MaxWidth { get { return 1; } }
	}
}